package com.easylinkin.linkappapi.elasticsearch.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.common.utils.NumberUtil;
import com.easylinkin.linkappapi.elasticsearch.constant.EsConstantEnum;
import com.easylinkin.linkappapi.elasticsearch.entity.EsDeviceCollectModel;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.ParsedAggregation;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount;
import org.springframework.util.Assert;

/**
 * Es查询工具
 *
 * @author zhengwen
 **/
public class EsDeviceCollectUtil {

  public static void initRangeTime(RequestModel<EsDeviceCollectModel> requestModel) {
    EsDeviceCollectModel esDeviceCollectModel = requestModel.getCustomQueryParams();

    //统计类型
    Assert.notNull(esDeviceCollectModel.getCycleTypeEnum(), "统计周期类型不能为空");
    EsConstantEnum.CycleTypeEnum cycleTypeEnum = esDeviceCollectModel.getCycleTypeEnum();

    Date now = DateUtil.date();
    Date startTime = now;
    Date endTime = now;
    //根据类型重置时间周期
    switch (cycleTypeEnum) {
      case CUSTOM:
        Assert.notNull(esDeviceCollectModel.getStartTime(), "开始时间为空");
        Assert.notNull(esDeviceCollectModel.getEndTime(), "结束时间为空");
        startTime = DateUtil.beginOfSecond(esDeviceCollectModel.getStartTime());
        endTime = DateUtil.endOfSecond(esDeviceCollectModel.getEndTime());
        break;
      case DAY:
        startTime = DateUtil.beginOfDay(now);
        endTime = DateUtil.endOfDay(now);
        break;
      case WEEK:
        startTime = DateUtil.beginOfWeek(now);
        endTime = DateUtil.endOfWeek(now);
        break;
      case MONTH:
        startTime = DateUtil.beginOfMonth(now);
        endTime = DateUtil.endOfMonth(now);
        break;
      case YEAR:
        startTime = DateUtil.beginOfYear(now);
        endTime = DateUtil.endOfYear(now);
        break;
      default:
        //未知默认当天?还是抛出异常？
        startTime = DateUtil.beginOfDay(now);
        endTime = DateUtil.endOfDay(now);
        break;
    }
    esDeviceCollectModel.setStartTime(startTime);
    esDeviceCollectModel.setEndTime(endTime);

  }

  /**
   * 初始化处理terms与agg
   *
   * @param groupTerm    分组term
   * @param requestModel 查询入参对象
   * @param aggKeyList   aggList
   */
  public static void initAggFun(TermsAggregationBuilder groupTerm,
      RequestModel<EsDeviceCollectModel> requestModel, List<String> aggKeyList) {
    List<Map<String, String>> aggList = requestModel.getCustomQueryParams().getAggsList();
    if (CollectionUtil.isNotEmpty(aggList)) {
      for (int j = 0; j < aggList.size(); j++) {
        String aggKey = "";
        Map<String, String> aggMap = aggList.get(j);
        Iterator<String> it = aggMap.keySet().iterator();
        while (it.hasNext()) {
          AggregationBuilder tmpAggBuilder = null;
          String key = it.next();
          String val = aggMap.get(key);
          if (org.apache.commons.lang3.StringUtils.isNotBlank(key)
              && org.apache.commons.lang3.StringUtils.isNotBlank(val)) {
            switch (val) {
              case "avg":
                aggKey = "avg_" + key;
                tmpAggBuilder = AggregationBuilders.avg(aggKey).field(key);
                break;
              case "count":
                aggKey = "count_" + key;
                tmpAggBuilder = AggregationBuilders.count(aggKey).field(key);
                break;
              case "sum":
                aggKey = "sum_" + key;
                tmpAggBuilder = AggregationBuilders.sum(aggKey).field(key);
                break;
              default:
                break;
            }
            if (tmpAggBuilder != null) {

              aggKeyList.add(aggKey);
              //聚合没有先后
              //分组条件连上聚合计算
              groupTerm.subAggregation(tmpAggBuilder);

            }
          }
        }
      }
    }
  }

  /**
   * 解析ES查询结果
   *
   * @param searchResponse ES查询结果
   * @param groupKeyList   分组keyList
   * @param aggKeyList     聚合keyList
   */
  public static List<Map<String, Object>> analyzeSearchResponse(
      SearchResponse searchResponse, List<String> groupKeyList, List<String> aggKeyList) {
    List<Map<String, Object>> dataList = new ArrayList<>();
    if (CollectionUtil.isNotEmpty(groupKeyList) && CollectionUtil.isNotEmpty(aggKeyList)) {

      List<? extends Terms.Bucket> lastBucket = null;
      Map<String, Object> groupDataMp = new HashMap<>();
      String lastGroupKey = "";

      Aggregations aggregations = searchResponse.getAggregations();
      for (int i = 0; i < groupKeyList.size(); i++) {
        //数据在最里层的buckets里
        String groupKey = groupKeyList.get(i);
        String[] groupKeyArr = groupKey.split("-");
        Terms aggGroups = aggregations.get(groupKey);
        List<? extends Terms.Bucket> buckets = aggGroups.getBuckets();
        if (CollectionUtil.isNotEmpty(buckets)) {
          Terms.Bucket bucket = buckets.get(0);
          groupDataMp.put("dataCount", bucket.getDocCount());

          aggregations = bucket.getAggregations();

          if (i == groupKeyList.size() - 1) {
            //最后一级分组
            lastGroupKey = groupKeyArr[1];
            lastBucket = buckets;
          } else {
            groupDataMp.put(groupKeyArr[1], bucket.getKey());
          }
        }
      }
      //读取最终数据
      dataList = readSearchData(lastGroupKey, lastBucket, groupDataMp, aggKeyList);
    }
    return dataList;
  }

  /**
   * 读取ES查询结果
   *
   * @param lastGroupKey 最后一个分组key
   * @param lastBucket   最里层的bucket
   * @param groupDataMp  分组数据map
   * @param aggKeyList   聚合key list
   */
  private static List<Map<String, Object>> readSearchData(String lastGroupKey,
      List<? extends Bucket> lastBucket,
      Map<String, Object> groupDataMp, List<String> aggKeyList) {
    List<Map<String, Object>> ls = new ArrayList<>();
    for (Terms.Bucket bucket : lastBucket) {
      if (CollectionUtil.isNotEmpty(aggKeyList)) {
        Object obj = new Object();
        Map<String, Object> mp = new HashMap<>();
        mp.putAll(groupDataMp);
        mp.put(lastGroupKey, bucket.getKey());
        for (int i = 0; i < aggKeyList.size(); i++) {
          mp.put("dataCount", bucket.getDocCount());

          String aggKey = aggKeyList.get(i);

          //这里值处理count条数处理为long，其余均有可能是小数或整数
          ParsedAggregation pa = bucket.getAggregations().get(aggKey);
          //解析聚合统计值，其实有些有特殊精度要求可能要做差异处理
          obj = parsedAggValue(pa);

          mp.put(aggKey, obj);
        }
        ls.add(mp);
      }
    }
    return ls;
  }

  /**
   * 解析聚合统计值
   *
   * @param pa 聚合统计值对象
   * @return object对象
   */
  private static Object parsedAggValue(ParsedAggregation pa) {
    Object obj = null;
    String pat = pa.getType();
    switch (pat) {
      case "sum":
        ParsedSum sum = (ParsedSum) pa;
        double douSum = sum.getValue();
        if ("Infinity".equals(douSum + "")) {
          //obj = "Infinity";
          obj = null;
        } else {
          obj = BigDecimal.valueOf(douSum);
          obj = NumberUtil.bigDecimalRoundHalfUp((BigDecimal) obj, 2);
        }
        break;
      case "avg":
        ParsedAvg avg = (ParsedAvg) pa;
        double douAvg = avg.getValue();
        if ("Infinity".equals(douAvg + "")) {
          //obj = "Infinity";
          obj = null;
        } else {
          obj = BigDecimal.valueOf(douAvg);
          obj = NumberUtil.bigDecimalRoundHalfUp((BigDecimal) obj, 2);
        }
        break;
      case "value_count":
        ParsedValueCount count = (ParsedValueCount) pa;
        long countValue = count.getValue();
        if ("Infinity".equals(countValue + "")) {
          //obj = "Infinity";
          obj = null;
        } else {
          obj = countValue;
        }
        break;
      default:

        break;
    }
    return obj;
  }

}
